package com.tcm.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.bo.BattleItemExcel;
import com.tcm.entity.dto.BattleItemUpdateDto;
import com.tcm.entity.dto.param.BattleItemPageParam;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.*;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.mapper.BattleItemAnswerMapper;
import com.tcm.mapper.BattleItemDetailMapper;
import com.tcm.service.BattleItemService;
import com.tcm.mapper.BattleItemMapper;
import com.tcm.service.BookService;
import com.tcm.service.CodeService;
import com.tcm.utils.ExcelExportUtil;
import com.tcm.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Guqier
 * @description 针对表【ts_battle_item(PK对战题库主表)】的数据库操作Service实现
 * @createDate 2023-09-10 14:24:47
 */
@Service
@Slf4j
public class BattleItemServiceImpl extends ServiceImpl<BattleItemMapper, BattleItem>
        implements BattleItemService {

    @Autowired
    private BattleItemDetailMapper battleItemDetailMapper;

    @Autowired
    private BattleItemAnswerMapper battleItemAnswerMapper;

    @Autowired
    private BookService bookService;

    @Autowired
    private CodeService codeService;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 添加对战题目
     * @param dto        对战题目数据封装
     * @param currUserId 当前用户ID
     * @return 添加结果，true为成功
     */
    @Override
    @Transactional
    public boolean addBattleItem(BattleItemUpdateDto dto, Long currUserId) {
        // 构造对战题目实体
        BattleItem battleItem = new BattleItem();
        BeanUtils.copyProperties(dto, battleItem);
        // 判断题目类型
        if (dto.getType().equals(AppProperties.ITEM_TYPE_SUBJECT)) {
            // 判断前驱题目是否存在
            if (Objects.nonNull(dto.getPrecursorId())) {
                // 前驱题目ID不为空，则判断前驱题目是否存在
                BattleItem preBattleItem = this.getById(dto.getPrecursorId());
                if (Objects.isNull(preBattleItem)) {
                    throw new EntityInvalidException("前驱题目不存在");
                }
                battleItem.setHasPrecursor(1);// 设置前驱题目
                battleItem.setGroupId(preBattleItem.getGroupId());// 题目分组ID与前驱题目一致
            } else {
                // 没有前驱题目，则生成groupId
                battleItem.setHasPrecursor(0);
                long groupId = IdUtil.getSnowflake().nextId();
                battleItem.setGroupId(groupId);
            }
        }
        battleItem.setCreateUser(currUserId);
        battleItem.setUpdateUser(currUserId);
        if (!this.save(battleItem)) {
            throw new DatabaseOperationException("题目保存失败");
        }
        // 保存题目详细信息
        List<BattleItemUpdateDto.Option> options = dto.getOptions();
        for (BattleItemUpdateDto.Option option : options) {
            BattleItemDetail detail = new BattleItemDetail();
            BeanUtils.copyProperties(option, detail);
            detail.setBattleItemId(battleItem.getId());
            detail.setCreateUser(currUserId);
            detail.setUpdateUser(currUserId);
            if (battleItemDetailMapper.insert(detail) != 1) {
                throw new DatabaseOperationException("题目选项保存失败");
            }
        }
        // 保存题目答案
        BattleItemAnswer answer = new BattleItemAnswer();
        answer.setBattleItemId(battleItem.getId());
        answer.setAns(dto.getAns());
        answer.setDescribe(dto.getDescribe());
        answer.setCreateUser(currUserId);
        answer.setUpdateUser(currUserId);
        if (battleItemAnswerMapper.insert(answer) != 1) {
            throw new DatabaseOperationException("题目答案保存失败");
        }
        return true;
    }

    /**
     * 添加对战题目
     * @param dto        对战题目数据封装
     * @param currUserId 当前用户ID
     * @return 修改结果，true为成功
     */
    @Override
    @Transactional
    @CacheEvict(value = AppProperties.ENTITY_CACHE_ITEM_BATTLE, key = "#dto.id", condition = "#dto.id != null")
    public boolean modifyBattleItem(BattleItemUpdateDto dto, Long currUserId) {
        // 获取题目信息
        BattleItem battleItem = this.getById(dto.getId());
        if (battleItem == null) {
            throw new EntityInvalidException("题目不存在");
        }
        // 判断题目类型
        if (dto.getType().equals(AppProperties.ITEM_TYPE_SUBJECT)) {
            if (dto.getPrecursorId() != null) {
                if (!Objects.equals(dto.getPrecursorId(), battleItem.getPrecursorId())) {
                    // 前驱题目ID不为空，则判断前驱题目是否存在
                    BattleItem preBattleItem = this.getById(dto.getPrecursorId());
                    if (Objects.isNull(preBattleItem)) {
                        throw new EntityInvalidException("前驱题目不存在");
                    }
                    battleItem.setPrecursorId(preBattleItem.getId());
                    battleItem.setHasPrecursor(1);// 设置前驱题目
                    battleItem.setGroupId(preBattleItem.getGroupId());// 题目分组ID与前驱题目一致
                }
            } else {
                battleItem.setPrecursorId(null);
                battleItem.setHasPrecursor(0);
                if (Objects.isNull(battleItem.getGroupId())) {
                    // 没有前驱题目，且没有分组ID，则生成分组ID
                    long groupId = IdUtil.getSnowflake().nextId();
                    battleItem.setGroupId(groupId);
                }
            }
        }
        // 修改其他属性
        battleItem.setType(dto.getType());
        battleItem.setContent(dto.getContent());
        battleItem.setSubContent(dto.getSubContent());
        battleItem.setDifficultyId(dto.getDifficultyId());
        battleItem.setChapterId(dto.getChapterId());
        battleItem.setUpdateUser(currUserId);
        if (!this.updateById(battleItem)) {
            throw new DatabaseOperationException("题目修改失败");
        }
        // 修改题目选项
        LambdaQueryWrapper<BattleItemDetail> queryWrapper = new LambdaQueryWrapper<BattleItemDetail>().eq(BattleItemDetail::getBattleItemId, battleItem.getId());
        Integer count = battleItemDetailMapper.selectCount(queryWrapper);
        int delete = battleItemDetailMapper.delete(queryWrapper);
        if (count != delete) {
            throw new DatabaseOperationException("题目选项修改失败");
        }
        List<BattleItemUpdateDto.Option> options = dto.getOptions();
        for (BattleItemUpdateDto.Option option : options) {
            BattleItemDetail detail = new BattleItemDetail();
            BeanUtils.copyProperties(option, detail);
            detail.setBattleItemId(battleItem.getId());
            detail.setCreateUser(currUserId);
            detail.setUpdateUser(currUserId);
            if (battleItemDetailMapper.insert(detail) != 1) {
                throw new DatabaseOperationException("题目选项保存失败");
            }
        }
        // 修改题目答案
        BattleItemAnswer answer = battleItemAnswerMapper.selectOne(new LambdaQueryWrapper<BattleItemAnswer>().eq(BattleItemAnswer::getBattleItemId, battleItem.getId()));
        answer.setAns(dto.getAns());
        answer.setDescribe(dto.getDescribe());
        answer.setUpdateUser(currUserId);
        if (battleItemAnswerMapper.updateById(answer) != 1) {
            throw new DatabaseOperationException("题目答案修改失败");
        }
        return true;
    }

    /**
     * 批量删除对战题目
     * @param ids 对战题目ID集合
     * @return 删除结果，true为成功
     */
    @Override
    @Transactional
    public boolean removeBattleItem(List<Long> ids) {
        for (Long id : ids) {
            BattleItem battleItem = this.getById(id);
            if (Objects.isNull(battleItem)) {
                throw new EntityInvalidException("题目不存在");
            }
            // 删除题目实体
            if (!this.removeBattleItemById(id)) {
                throw new DatabaseOperationException("删除题目失败");
            }
            // 删除题目选项
            LambdaQueryWrapper<BattleItemDetail> queryWrapper = new LambdaQueryWrapper<BattleItemDetail>().eq(BattleItemDetail::getBattleItemId, battleItem.getId());
            Integer count = battleItemDetailMapper.selectCount(queryWrapper);
            int delete = battleItemDetailMapper.delete(queryWrapper);
            if (count != delete) {
                throw new DatabaseOperationException("题目选项删除失败");
            }
            // 删除题目答案
            if (battleItemAnswerMapper.delete(new LambdaQueryWrapper<BattleItemAnswer>().eq(BattleItemAnswer::getBattleItemId, battleItem.getId())) != 1) {
                throw new DatabaseOperationException("题目答案删除失败");
            }
        }
        return true;
    }

    /**
     * 根据ID删除对战题目（缓存）
     * @param id 对战题目ID
     * @return 删除结果
     */
    @Override
    @CacheEvict(value = AppProperties.ENTITY_CACHE_ITEM_BATTLE, key = "#id", condition = "#id != null")
    public boolean removeBattleItemById(Long id) {
        return this.removeById(id);
    }

    /**
     * 根据ID获取对战题目
     * @param id 对战题目ID
     * @return 对战题目
     */
    @Override
    @Cacheable(value = AppProperties.ENTITY_CACHE_ITEM_BATTLE, key = "#id", unless = "#result == null")
    public BattleItem getBattleItemById(Long id) {
        return this.getById(id);
    }

    /**
     * 分页获取对战题目列表
     * @param param 分页查询参数
     * @return 对战题目列表
     */
    @Override
    public PageR<BattleItemListResult> getBattleItemListByPage(BattleItemPageParam param) {
        // 构造查询条件
        Page<BattleItem> page = new Page<>(param.getPage(), param.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));
        LambdaQueryWrapper<BattleItem> queryWrapper = buildBattleItemListQueryWrapper(param);
        this.page(page, queryWrapper);
        // 构造结果集
        PageR<BattleItemListResult> pageR = new PageR<>();
        BeanUtils.copyProperties(page, pageR, "records");
        List<BattleItem> records = page.getRecords();
        pageR.setRecords(transferBattleItemToListResult(records));
        return pageR;
    }

    /**
     * 根据参数构建对战题目查询条件
     * @param param 查询参数
     * @return LambdaQueryWrapper<BattleItem>
     */
    private LambdaQueryWrapper<BattleItem> buildBattleItemListQueryWrapper(BattleItemPageParam param) {
        LambdaQueryWrapper<BattleItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Objects.nonNull(param.getType()), BattleItem::getType, param.getType());

        if (Objects.nonNull(param.getChapterId())){
            // 获取当前选择书籍节点下所有子节点
            List<Long> bookIds = bookService.getChildBooks(param.getChapterId()).stream().map(Book::getId).collect(Collectors.toList());
            bookIds.add(param.getChapterId());
            queryWrapper.in(BattleItem::getChapterId, bookIds);
        }

        queryWrapper.like(StringUtils.hasText(param.getContent()), BattleItem::getContent, param.getContent());
        queryWrapper.eq(Objects.nonNull(param.getDifficultyId()), BattleItem::getDifficultyId, param.getDifficultyId());
        return queryWrapper;
    }

    /**
     * 将对战题目实体类封装为列表展示类
     * @param battleItems 对战题目集合
     * @return 列表展示类
     */
    private List<BattleItemListResult> transferBattleItemToListResult(List<BattleItem> battleItems) {
        // 获取书籍信息
        List<Book> bookList = bookService.getBookList();

        List<Long> ids = battleItems.stream().map(BattleItem::getId).collect(Collectors.toList());
        // 获取所有题目选项信息
        List<BattleItemDetail> details = battleItemDetailMapper.selectList(new LambdaQueryWrapper<BattleItemDetail>()
                .in(!CollectionUtils.isEmpty(ids), BattleItemDetail::getBattleItemId, ids)
                .apply(CollectionUtils.isEmpty(ids), "1 != 1"));
        // 获取所有题目答案信息
        List<BattleItemAnswer> answers = battleItemAnswerMapper.selectList(new LambdaQueryWrapper<BattleItemAnswer>()
                .in(!CollectionUtils.isEmpty(ids), BattleItemAnswer::getBattleItemId, ids)
                .apply(CollectionUtils.isEmpty(ids), "1 != 1"));

        // 构造结果集
        return battleItems.stream().map(battleItem -> {
            BattleItemListResult result = new BattleItemListResult();
            BeanUtils.copyProperties(battleItem, result);
            // 查询前驱题目子题干
            if (Objects.nonNull(result.getHasPrecursor()) && result.getHasPrecursor().equals(1) && Objects.nonNull(result.getPrecursorId())) {
                BattleItem preBattleItem = this.getById(result.getPrecursorId());
                Optional.ofNullable(preBattleItem).ifPresent(pre -> result.setPrecursorSubContent(pre.getSubContent()));
            }
            // 查询题目所属章节
            List<Book> books = new ArrayList<>();
            bookService.getBookAllParentNodesIdByRecursion(result.getChapterId(), bookList, books);
            Collections.reverse(books);
            String chapter = books.stream().map(Book::getName).collect(Collectors.joining("/"));
            result.setChapter(chapter);
            result.setBookIds(books.stream().map(Book::getId).collect(Collectors.toList()));
            // 查询题目难易级别
            Code code = codeService.getCodeById(result.getDifficultyId());
            result.setDifficulty(code.getName());
            // 查询题目选项
            List<BattleItemListResult.Option> options = details.stream().filter(detail -> detail.getBattleItemId().equals(battleItem.getId())).map(detail -> new BattleItemListResult.Option(detail.getOption(), detail.getContent())).collect(Collectors.toList());
            result.setOptions(options);
            // 查询题目答案
            answers.stream().filter(answer -> answer.getBattleItemId().equals(battleItem.getId())).findFirst().ifPresent(answer -> {
                result.setAns(answer.getAns());
                result.setDescribe(answer.getDescribe());
            });
            return result;
        }).collect(Collectors.toList());
    }

    /**
     * 批量导入对战题目
     * @param file excel文件
     * @return 导入结果
     * @throws IOException 异常
     */
    @Override
    public BatchImportResult<BattleItemExcel> importBattleItem(MultipartFile file) throws IOException {
        // 解析Excel文件，获取题目信息
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        reader.addHeaderAlias("序号", "no");
        reader.addHeaderAlias("是否存在前驱题目", "hasPrecursor");
        reader.addHeaderAlias("前驱题目序号", "precursorNo");
        reader.addHeaderAlias("题目类型", "type");
        reader.addHeaderAlias("题干", "content");
        reader.addHeaderAlias("次级题干", "subContent");
        reader.addHeaderAlias("难易级别", "difficulty");
        reader.addHeaderAlias("章节", "chapter");
        reader.addHeaderAlias("选项", "options");
        reader.addHeaderAlias("答案", "ans");
        reader.addHeaderAlias("答案解析", "describe");
        List<BattleItemExcel> battleItemExcels = reader.readAll(BattleItemExcel.class);
        // 获取书籍信息
        List<Book> bookList = bookService.getBookList();
        // 获取难易级别信息
        LambdaQueryWrapper<Code> codeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        codeLambdaQueryWrapper.eq(Code::getType, AppProperties.CODE_TYPE_DIFFICULTY);
        List<Code> difficultyList = codeService.list(codeLambdaQueryWrapper);
        // 构造结果集
        BatchImportResult<BattleItemExcel> result = new BatchImportResult<>();
        List<BattleItemExcel> successList = new ArrayList<>();
        List<BattleItemExcel> failList = new ArrayList<>();
        List<BattleItemUpdateDto> dtoList = new ArrayList<>();

        // 生成序号-主键（雪花）映射关系
        Map<Integer, Long> idNoMap = new HashMap<>();
        battleItemExcels.stream().map(BattleItemExcel::getNo).forEach(no -> {
            idNoMap.put(no, IdUtil.getSnowflake().nextId());
        });

        // 校验题目信息
        for (BattleItemExcel excel : battleItemExcels) {
            BattleItemUpdateDto dto = new BattleItemUpdateDto();
            dto.setId(idNoMap.get(excel.getNo()));
            // 校验题目题目类型
            if (excel.getType().equals("经典案例实训")) {
                dto.setType(AppProperties.ITEM_TYPE_SUBJECT);
                if (!StringUtils.hasText(excel.getHasPrecursor())) {
                    excel.setMsg("是否存在前驱题目为空");
                    failList.add(excel);
                    continue;
                }
                // 判断是否存在前驱题目
                if (excel.getHasPrecursor().equals("是")) {
                    Long preId = idNoMap.get(excel.getPrecursorNo());
                    if (Objects.isNull(preId)) {
                        excel.setMsg("前驱题目序号错误");
                        failList.add(excel);
                        continue;
                    }
                    dto.setPrecursorId(preId);
                    dto.setHasPrecursor(1);
                } else {
                    dto.setHasPrecursor(0);
                }
            } else if (excel.getType().equals("原文实训")) {
                dto.setType(AppProperties.ITEM_TYPE_CLOZE);
            } else {
                excel.setMsg("题目类型不存在");
                failList.add(excel);
                continue;
            }
            // 校验章节
            Optional<Book> bookOptional = bookList.stream().filter(book -> book.getName().equals(excel.getChapter())).findFirst();
            if (!bookOptional.isPresent()) {
                excel.setMsg("章节不存在");
                failList.add(excel);
                continue;
            }
            // 校验难易级别
            Optional<Code> codeOptional = difficultyList.stream().filter(code -> code.getName().equals(excel.getDifficulty())).findFirst();
            if (!codeOptional.isPresent()) {
                excel.setMsg("难易级别不存在");
                failList.add(excel);
                continue;
            }
            // 解析选项
            List<BattleItemUpdateDto.Option> optionList = null;
            try {
                String[] options = excel.getOptions().split(";");
                optionList = new ArrayList<>();
                for (String option : options) {
                    String[] s = option.split("\\|");
                    BattleItemUpdateDto.Option dtoOption = new BattleItemUpdateDto.Option(s[0], s[1]);
                    optionList.add(dtoOption);
                }
            } catch (Exception e) {
                excel.setMsg("选项格式不正确");
                failList.add(excel);
                continue;
            }
            dto.setChapterId(bookOptional.get().getId());
            dto.setDifficultyId(codeOptional.get().getId());
            dto.setOptions(optionList);
            dto.setContent(excel.getContent());
            dto.setSubContent(excel.getSubContent());
            dto.setAns(excel.getAns());
            dto.setDescribe(excel.getDescribe());

            // 通过校验，加入成功集
            excel.setMsg("校验通过");
            successList.add(excel);
            // 保存Dto
            dtoList.add(dto);
        }
        // 构造结果集
        result.setUuid(UUID.randomUUID().toString());
        result.setTotal(battleItemExcels.size());
        result.setSuccessCount(successList.size());
        result.setSuccessList(successList);
        result.setFailCount(failList.size());
        result.setFailList(failList);
        // 存入缓存，等待前端确认，超过5分钟自动清除
        redisUtil.setCacheObject(CachePrefix.PC_IMPORT_BATTLE_RES.getCacheKey(result.getUuid()), result, 5, TimeUnit.MINUTES);
        redisUtil.setCacheObject(CachePrefix.PC_IMPORT_BATTLE_DTO.getCacheKey(result.getUuid()), dtoList, 5, TimeUnit.MINUTES);
        log.info("对战题目上传ID：{} 存入缓存", result.getUuid());
        return result;
    }

    /**
     * 确认导入对战题目
     * @param uuid       导入uuid
     * @param currUserId 当前登录用户ID
     * @return 导入结果
     */
    @Override
    @Transactional
    public boolean confirmBatchBattleItem(String uuid, Long currUserId) {
        // 从缓存中取出数据
        List<BattleItemUpdateDto> dtos = redisUtil.getCacheObject(CachePrefix.PC_IMPORT_BATTLE_DTO.getCacheKey(uuid));
        if (dtos == null) {
            return false;// 缓存不存在
        }
//        // 保存数据
//        List<Long> savedIds = new ArrayList<>();
//        // 先保存没有前驱题目的
//        List<BattleItemUpdateDto> noPreItemDtos = dtos.stream().filter(dto -> dto.getType().equals(AppProperties.ITEM_TYPE_CLOZE) || (dto.getType().equals(AppProperties.ITEM_TYPE_SUBJECT) && dto.getHasPrecursor().equals(0))).collect(Collectors.toList());
//        for (BattleItemUpdateDto dto : noPreItemDtos) {
//            if (this.addBattleItem(dto, currUserId)) {
//                savedIds.add(dto.getId());
//            }
//        }
//        List<BattleItemUpdateDto> dtoList = dtos.stream().filter(dto -> !savedIds.contains(dto.getId())).collect(Collectors.toList());
//        // 保存剩余题目
//        while (savedIds.size() != dtos.size()) {
//            for (BattleItemUpdateDto dto : dtoList) {
//                if (this.addBattleItem(dto, currUserId)) {
//                    savedIds.add(dto.getId());
//                }
//            }
//        }
        int cnt = 0;
        for (BattleItemUpdateDto dto : dtos) {
            if (addBattleItem(dto, currUserId)) {
                cnt++;
            }
        }
        if (cnt != dtos.size()){
            throw new DatabaseOperationException("数据库操作失败");
        }
        // 保存成功则清除缓存
        redisUtil.deleteObject(CachePrefix.PC_IMPORT_BATTLE_DTO.getCacheKey(uuid));
        return true;
    }

    /**
     * 导出校验失败对战题目信息
     * @param uuid     导入uuid
     * @param response 响应对象
     * @throws Exception 异常
     */
    @Override
    public void exportImportFailedBattleItem(String uuid, HttpServletResponse response) throws Exception {
        // 从缓存中获取数据
        BatchImportResult<BattleItemExcel> result = redisUtil.getCacheObject(CachePrefix.PC_IMPORT_BATTLE_RES.getCacheKey(uuid));
        if (result == null) {
            throw new Exception("缓存不存在或已过期");
        }
        exportBattleItemCommon(result.getFailList(), response);
    }

    /**
     * 通用队长题目导出
     * @param excels   对战题目Excel数据封装类
     * @param response 响应对象
     * @throws IOException IO异常
     */
    public void exportBattleItemCommon(List<BattleItemExcel> excels, HttpServletResponse response) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter();
        writer.addHeaderAlias("no", "序号");
        writer.addHeaderAlias("hasPrecursor", "是否存在前驱题目");
        writer.addHeaderAlias("precursorNo", "前驱题目序号");
        writer.addHeaderAlias("type", "题目类型");
        writer.addHeaderAlias("content", "题干");
        writer.addHeaderAlias("subContent", "次级题干");
        writer.addHeaderAlias("difficulty", "难易级别");
        writer.addHeaderAlias("chapter", "章节");
        writer.addHeaderAlias("options", "选项");
        writer.addHeaderAlias("ans", "答案");
        writer.addHeaderAlias("describe", "答案解析");
        writer.addHeaderAlias("msg", "导入/导出状态");

        // 响应数据
        writer.write(excels, true);

        // 导出数据
        ExcelExportUtil.commonExport(writer, response);
    }

    /**
     * 导出对战题信息
     * @param param    条件参数封装
     * @param response 响应对象
     */
    @Override
    public void exportBattleItem(BattleItemPageParam param, HttpServletResponse response) throws IOException {
        // 查询符合条件的题目
        LambdaQueryWrapper<BattleItem> queryWrapper = buildBattleItemListQueryWrapper(param);
        List<BattleItem> battleItems = this.list(queryWrapper);
        // 获取书籍列表
        List<Book> bookList = bookService.getBookList();

        // 构造结果集
        List<BattleItemExcel> excels = new ArrayList<>();
        Map<Long, Integer> idNoMap = new HashMap<>();
        // 构建题目序号映射
        for (int i = 0; i < battleItems.size(); i++) {
            idNoMap.put(battleItems.get(i).getId(), i + 1);
        }
        for (int i = 0; i < battleItems.size(); i++) {
            BattleItem battleItem = battleItems.get(i);
            Integer no = idNoMap.get(battleItem.getId());
            BattleItemExcel excel = new BattleItemExcel();
            excel.setNo(no);
            // 判断题目类型
            if (battleItem.getType().equals(AppProperties.ITEM_TYPE_SUBJECT)) {
                excel.setType("经典案例实训");
                if (battleItem.getHasPrecursor() == 0) {
                    excel.setHasPrecursor("否");
                } else {
                    excel.setHasPrecursor("是");
                    excel.setPrecursorNo(idNoMap.get(battleItem.getPrecursorId()));
                }
                excel.setSubContent(battleItem.getSubContent());
            } else {
                excel.setType("原文实训");
            }
            excel.setContent(battleItem.getContent());
            excel.setDifficulty(codeService.getCodeById(battleItem.getDifficultyId()).getName());
            bookList.stream().filter(book -> book.getId().equals(battleItem.getChapterId())).findFirst().ifPresent(book -> excel.setChapter(book.getName()));

            // 查询题目选项
            List<BattleItemDetail> details = battleItemDetailMapper.selectList(new LambdaQueryWrapper<BattleItemDetail>().eq(BattleItemDetail::getBattleItemId, battleItem.getId()));
            excel.setOptions(details.stream().map(battleItemDetail -> battleItemDetail.getOption() + "|" + battleItemDetail.getContent()).collect(Collectors.joining(";")));

            // 查询题目答案
            BattleItemAnswer answer = battleItemAnswerMapper.selectOne(new LambdaQueryWrapper<BattleItemAnswer>().eq(BattleItemAnswer::getBattleItemId, battleItem.getId()));
            excel.setAns(answer.getAns());
            excel.setDescribe(answer.getDescribe());
            excel.setMsg("正常导出");
            excels.add(excel);
        }
        // 导出
        exportBattleItemCommon(excels, response);
    }

    /**
     * 将对战题转换为题目响应类
     * @param battleItem 对战题对象
     * @return 题目相应类对象
     */
    @Override
    public BattleTopicResp transferBattleItemToTopicResp(BattleItem battleItem) {
        List<BattleItemDetail> battleItemDetails = battleItemDetailMapper.selectList(new LambdaQueryWrapper<BattleItemDetail>().eq(BattleItemDetail::getBattleItemId, battleItem.getId()));
        List<BattleTopicResp.Option> options = battleItemDetails.stream().map(detail -> new BattleTopicResp.Option(detail.getOption(), detail.getContent())).collect(Collectors.toList());
        BattleTopicResp resp = new BattleTopicResp();
        BeanUtils.copyProperties(battleItem, resp);
        resp.setId(battleItem.getId().toString());
        resp.setIsMultiple(this.isMultiple(battleItem.getId()));
        resp.setDifficulty(codeService.getCodeById(battleItem.getDifficultyId()).getName());
        resp.setOptions(options);
        return resp;
    }

    /**
     * 批改对战题答案
     * @param battleItemId 题目ID
     * @param ans          用户答案
     * @return 批改结果
     */
    @Override
    public BattleItemCorrectResp correctBattleItem(Long battleItemId, String ans) {
        int score = 0;
        // 查询该题答案
        BattleItemAnswer answer = battleItemAnswerMapper.selectOne(new LambdaQueryWrapper<BattleItemAnswer>().eq(BattleItemAnswer::getBattleItemId, battleItemId));
        if (answer.getAns().equalsIgnoreCase(ans)) {
            score = AppProperties.BATTLE_ITEM_SCORE;
        }
        return new BattleItemCorrectResp().setScore(score).setStandardAns(answer.getAns()).setDescribe(answer.getDescribe());
    }

    /**
     * 判断是否是多选
     * @param battleItemId 题目ID
     * @return 是否是多选
     */
    @Override
    public Boolean isMultiple(Long battleItemId){
        BattleItemAnswer answer = battleItemAnswerMapper.selectOne(new LambdaQueryWrapper<BattleItemAnswer>().eq(BattleItemAnswer::getBattleItemId, battleItemId));
        if (answer.getAns().length() > 1){
            return true;
        }
        return false;
    }

    /**
     * 将对战题目转换为解析响应类
     * @param battleItems 对战题目集合
     * @return 题目解析相应类集合
     */
    @Override
    public List<BattleItemDescribeResp> transferBattleItemToDescribeResp(List<BattleItem> battleItems){
        List<Long> ids = battleItems.stream().map(BattleItem::getId).collect(Collectors.toList());
        // 准备数据
        List<BattleItemDetail> details = battleItemDetailMapper.selectList(new LambdaQueryWrapper<BattleItemDetail>()
                .in(!CollectionUtils.isEmpty(ids), BattleItemDetail::getBattleItemId, ids)
                .apply(CollectionUtils.isEmpty(ids), "1 != 1"));
        List<BattleItemAnswer> answers = battleItemAnswerMapper.selectList(new LambdaQueryWrapper<BattleItemAnswer>()
                .in(!CollectionUtils.isEmpty(ids), BattleItemAnswer::getBattleItemId, ids)
                .apply(CollectionUtils.isEmpty(ids), "1 != 1"));
        // 构造题目结果集
        return battleItems.stream().map(battleItem -> {
            BattleItemDescribeResp resp = new BattleItemDescribeResp();
            resp.setId(String.valueOf(battleItem.getId()))
                    .setType(battleItem.getType())
                    .setContent(battleItem.getContent())
                    .setSubContent(battleItem.getSubContent())
                    .setDifficulty(codeService.getCodeById(battleItem.getDifficultyId()).getName())
                    .setChapter(bookService.getById(battleItem.getChapterId()).getName());
            List<BattleItemDescribeResp.Option> options = details.stream()
                    .filter(d -> d.getBattleItemId().equals(battleItem.getId()))
                    .map(detail -> new BattleItemDescribeResp.Option(detail.getOption(), detail.getContent()))
                    .collect(Collectors.toList());
            resp.setOptions(options);
            answers.stream()
                    .filter(a -> a.getBattleItemId().equals(battleItem.getId()))
                    .findFirst()
                    .ifPresent(answer -> {
                        resp.setAns(answer.getAns());
                        resp.setDescribe(answer.getDescribe());
                    });
            return resp;
        }).collect(Collectors.toList());
    }
}




